package com.gitee.wsl.ext.list

fun List<Any>?.isNullOrEmpty(): Boolean {
    if (this == null) return true
    return this.isEmpty()
}

fun <T> List<T>.startsWith(prefix: List<T>): Boolean =
    size >= prefix.size && subList(0, prefix.size) == prefix


fun <T> List<T>.endsWith(suffix: List<T>): Boolean =
    size >= suffix.size && subList(size - suffix.size, size) == suffix


fun List<*>.areElementsDistinct() =
    // There is a more efficient implementation.
    distinct().size == size

/**
 * Checks if the contents of the list is the same as the given array.
 */
fun List<*>.contentEquals(other: Array<*>): Boolean {
    if (size != other.size) return false
    for (i in indices) {
        if (get(i) != other[i]) return false
    }
    return true
}

fun List<*>.contentEquals(other: List<*>): Boolean {
    if (size != other.size) return false
    for (i in indices) {
        if (get(i) != other[i]) return false
    }
    return true
}


/**
 * Asserts that a collection contains a subset of items the same order, but may have other items in the list.
 *
 * Usages:
 *
 * - `[]` containsSubList `[1,2,3]` fails
 * - `[1,2,3]` containsSubList `[4,5,6]` fails
 * - `[1,2,3]` containsSubList `[]` pass
 * - `[]` containsSubList `[]` pass
 * - `[1,2]` containsSubList `[1,2,3]` pass
 * - `[2,3,4]` containsSubList `[1,2,3,4,5]` pass
 *
 * @param sublist The list of items the actual list should contain in the same order.
 */
fun List<*>.containsSubList(sublist: List<*>):Boolean{
    var sublistMatched = sublist.isEmpty()
    var target: List<*> = this

    while (!sublistMatched) {
        val matchOfFirstInTarget = target.indexOf(sublist.first())
        if (matchOfFirstInTarget == -1) break
        var n = 1
        while (n < sublist.size && matchOfFirstInTarget + n < target.size) {
            val a = target[matchOfFirstInTarget + n]
            val b = sublist[n]
            if (a != b) break
            n += 1
        }
        sublistMatched = (n == sublist.size)
        if (sublistMatched) break
        if (matchOfFirstInTarget + n == target.size) break
        target = target.subList(matchOfFirstInTarget + 1, target.size)
    }

    return sublistMatched

}

fun <T> List<T>.singleIs(predicate: (T) -> Boolean): Boolean =
    this.size == 1 && this.singleOrNull()?.let(predicate) ?: false

fun <T> List<T>.doubleIs(predicate: (T, T) -> Boolean): Boolean =
    this.size == 2 && predicate(this[0], this[1])